<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.4.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/blogwebpage/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/blogwebpage/images/favicon.ico">
  <link rel="icon" type="image/png" sizes="16x16" href="/blogwebpage/images/favicon.ico">
  <link rel="mask-icon" href="/blogwebpage/images/logo.svg" color="#222">
  <meta name="msvalidate.01" content="9BDlx6FiNE">

<link rel="stylesheet" href="/blogwebpage/css/main.css">


<link rel="stylesheet" href="/blogwebpage/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"wangkejie.gitee.io","root":"/blogwebpage/","scheme":"Gemini","version":"7.8.0","exturl":true,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":"default"},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="书山有路勤为径，学海无涯苦作舟！">
<meta property="og:type" content="website">
<meta property="og:title" content="王科杰">
<meta property="og:url" content="https://wangkejie.gitee.io/blogwebpage/page/2/index.html">
<meta property="og:site_name" content="王科杰">
<meta property="og:description" content="书山有路勤为径，学海无涯苦作舟！">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="Jack Wang">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="https://wangkejie.gitee.io/blogwebpage/page/2/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : true,
    isPost : false,
    lang   : 'zh-CN'
  };
</script>

  <title>王科杰 - Jack's Notes</title>
  


  <script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?93e26277de0cf2c5851167f2c6dcaa7d";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>




  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/blogwebpage/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">王科杰</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">Jack's Notes</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/blogwebpage/" rel="section"><i class="home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/blogwebpage/archives/" rel="section"><i class="archive fa-fw"></i>归档<span class="badge">52</span></a>

  </li>
        <li class="menu-item menu-item-about">

    <a href="/blogwebpage/me/" rel="section"><i class="user fa-fw"></i>关于</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/blogwebpage/tags/" rel="section"><i class="tags fa-fw"></i>标签<span class="badge">54</span></a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/blogwebpage/categories/" rel="section"><i class="th fa-fw"></i>分类<span class="badge">56</span></a>

  </li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div id="search-result">
  <div id="no-result">
    <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
  </div>
</div>

    </div>
  </div>

</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

  <span class="exturl github-corner" data-url="aHR0cHM6Ly9naXRodWIuY29tL0phY2stVmluZw==" title="Follow me on GitHub" aria-label="Follow me on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></span>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content index posts-expand">
            
      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://wangkejie.gitee.io/blogwebpage/iOS/multithreading/index.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blogwebpage/images/avatar.gif">
      <meta itemprop="name" content="Jack Wang">
      <meta itemprop="description" content="书山有路勤为径，学海无涯苦作舟！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="王科杰">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/blogwebpage/iOS/multithreading/index.html" class="post-title-link" itemprop="url">多线程</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2019-07-10 12:24:27" itemprop="dateCreated datePublished" datetime="2019-07-10T12:24:27+08:00">2019-07-10</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E5%A4%9A%E7%BA%BF%E7%A8%8B/" itemprop="url" rel="index"><span itemprop="name">多线程</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h2 id="进程"><a href="#进程" class="headerlink" title="进程"></a>进程</h2><ul>
<li>1.进程是一个具有一定独立功能的程序关于某次数据集合的一次运行活动，它是操作系统分配资源的基本单元.</li>
<li>2.进程是指在系统中正在运行的一个应用程序，就是一段程序的执行过程,我们可以理解为手机上的一个app.</li>
<li>3.每个进程之间是独立的，每个进程均运行在其专用且受保护的内存空间内，拥有独立运行所需的全部资源</li>
</ul>
<h2 id="线程"><a href="#线程" class="headerlink" title="线程"></a>线程</h2><ul>
<li>1.程序执行流的最小单元，线程是进程中的一个实体.</li>
<li>2.一个进程要想执行任务,必须至少有一条线程.应用程序启动的时候，系统会默认开启一条线程,也就是主线程
          <!--noindex-->
            <div class="post-button">
              <a class="btn" href="/blogwebpage/iOS/multithreading/index.html#more" rel="contents">
                阅读全文 &raquo;
              </a>
            </div>
          <!--/noindex-->
        
      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://wangkejie.gitee.io/blogwebpage/python/index.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blogwebpage/images/avatar.gif">
      <meta itemprop="name" content="Jack Wang">
      <meta itemprop="description" content="书山有路勤为径，学海无涯苦作舟！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="王科杰">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/blogwebpage/python/index.html" class="post-title-link" itemprop="url">Python</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2018-10-10 11:26:39" itemprop="dateCreated datePublished" datetime="2018-10-10T11:26:39+08:00">2018-10-10</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/Python/" itemprop="url" rel="index"><span itemprop="name">Python</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h1 id="Python"><a href="#Python" class="headerlink" title="Python"></a>Python</h1><p>Linux和Mac环境自带了Python 2.x版本，但是如果要更新到3.x的版本，可以在<span class="exturl" data-url="aHR0cHM6Ly93d3cucHl0aG9uLm9yZy8=">Python的官方网站<i class="fa fa-external-link-alt"></i></span>下载安装</p>
<h2 id="注释"><a href="#注释" class="headerlink" title="注释"></a>注释</h2><ol>
<li>单行注释 - 以#和空格开头的部分 </li>
<li>多行注释 - 三个引号开头，三个引号结尾</li>
</ol>
<h2 id="语言元素"><a href="#语言元素" class="headerlink" title="语言元素"></a>语言元素</h2><h3 id="变量和类型"><a href="#变量和类型" class="headerlink" title="变量和类型"></a>变量和类型</h3><ul>
<li>整型：Python中可以处理任意大小的整数<ul>
<li>（Python 2.x中有int和long两种类型的整数，但这种区分对Python来说意义不大，因此在Python 3.x中整数只有int这一种了），而且支持二进制（如<code>0b100</code>，换算成十进制是4）、八进制（如<code>0o100</code>，换算成十进制是64）、十进制（<code>100</code>）和十六进制（<code>0x100</code>，换算成十进制是256）的表示法。</li>
</ul>
</li>
<li>浮点型：浮点数也就是小数，之所以称为浮点数，是因为按照科学记数法表示时，一个浮点数的小数点位置是可变的，浮点数除了数学写法（如<code>123.456</code>）之外还支持科学计数法（如<code>1.23456e2</code>）。</li>
<li>字符串型：字符串是以单引号或双引号括起来的任意文本，比如<code>&#39;hello&#39;</code>和<code>&quot;hello&quot;</code>,字符串还有原始字符串表示法、字节字符串表示法、Unicode字符串表示法，而且可以书写成多行的形式（用三个单引号或三个双引号开头，三个单引号或三个双引号结尾）。</li>
<li>布尔型：布尔值只有<code>True</code>、<code>False</code>两种值，要么是<code>True</code>，要么是<code>False</code>，在Python中，可以直接用<code>True</code>、<code>False</code>表示布尔值（请注意大小写），也可以通过布尔运算计算出来（例如<code>3 &lt; 5</code>会产生布尔值<code>True</code>，而<code>2 == 1</code>会产生布尔值<code>False</code>）。</li>
<li>复数型：形如<code>3+5j</code>，跟数学上的复数表示一样，唯一不同的是虚部的<code>i</code>换成了<code>j</code>。</li>
</ul>
<p>在对变量类型进行转换时可以使用Python的内置函数（准确的说下面列出的并不是真正意义上的函数，而是后面我们要讲到的创建对象的构造方法）。</p>
<ul>
<li><code>int()</code>：将一个数值或字符串转换成整数，可以指定进制。</li>
<li><code>float()</code>：将一个字符串转换成浮点数。</li>
<li><code>str()</code>：将指定的对象转换成字符串形式，可以指定编码。</li>
<li><code>chr()</code>：将整数转换成该编码对应的字符串（一个字符）。</li>
<li><code>ord()</code>：将字符串（一个字符）转换成对应的编码（整数）。</li>
</ul>
<h3 id="运算符"><a href="#运算符" class="headerlink" title="运算符"></a>运算符</h3><blockquote>
<p><strong>说明：</strong> 在实际开发中，如果搞不清楚运算符的优先级，可以使用括号来确保运算的执行顺序。</p>
</blockquote>
<h2 id="分支结构"><a href="#分支结构" class="headerlink" title="分支结构"></a>分支结构</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">x = <span class="built_in">float</span>(<span class="built_in">input</span>(<span class="string">&#x27;x = &#x27;</span>))</span><br><span class="line"><span class="keyword">if</span> x &gt; <span class="number">1</span>:</span><br><span class="line">    y = <span class="number">3</span> * x - <span class="number">5</span></span><br><span class="line"><span class="keyword">elif</span> x &gt;= <span class="number">-1</span>:</span><br><span class="line">    y = x + <span class="number">2</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    y = <span class="number">5</span> * x + <span class="number">3</span></span><br><span class="line">print(<span class="string">&#x27;f(%.2f) = %.2f&#x27;</span> % (x, y))</span><br></pre></td></tr></table></figure>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">x = <span class="built_in">float</span>(<span class="built_in">input</span>(<span class="string">&#x27;x = &#x27;</span>))</span><br><span class="line"><span class="keyword">if</span> x &gt; <span class="number">1</span>:</span><br><span class="line">    y = <span class="number">3</span> * x - <span class="number">5</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="keyword">if</span> x &gt;= <span class="number">-1</span>:</span><br><span class="line">        y = x + <span class="number">2</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        y = <span class="number">5</span> * x + <span class="number">3</span></span><br><span class="line">print(<span class="string">&#x27;f(%.2f) = %.2f&#x27;</span> % (x, y))</span><br></pre></td></tr></table></figure>
<blockquote>
<p><strong>说明：</strong> 大家可以自己感受一下这两种写法到底是哪一种更好。在之前我们提到的Python之禅中有这么一句话“Flat is better than nested.”，之所以提倡代码“扁平化”是因为嵌套结构的嵌套层次多了之后会严重的影响代码的可读性，所以能使用扁平化的结构时就不要使用嵌套。</p>
</blockquote>
<h2 id="循环结构"><a href="#循环结构" class="headerlink" title="循环结构"></a>循环结构</h2><h3 id="for-in循环"><a href="#for-in循环" class="headerlink" title="for-in循环"></a>for-in循环</h3><p>如果明确的知道循环执行的次数或者要对一个容器进行迭代（后面会讲到），那么我们推荐使用<code>for-in</code>循环。</p>
<ul>
<li><code>range(101)</code>可以产生一个0到100的整数序列。</li>
<li><code>range(1, 100)</code>可以产生一个1到99的整数序列。</li>
<li><code>range(1, 100, 2)</code>可以产生一个1到99的奇数序列，其中的2是步长，即数值序列的增量。</li>
</ul>
<h3 id="while循环"><a href="#while循环" class="headerlink" title="while循环"></a>while循环</h3><p>如果要构造不知道具体循环次数的循环结构，我们推荐使用<code>while</code>循环。<code>while</code>循环通过一个能够产生或转换出<code>bool</code>值的表达式来控制循环，表达式的值为<code>True</code>循环继续，表达式的值为<code>False</code>循环结束。下面我们通过一个“猜数字”的小游戏（计算机出一个1~100之间的随机数，人输入自己猜的数字，计算机给出对应的提示信息，直到人猜出计算机出的数字）来看看如何使用<code>while</code>循环。</p>
<blockquote>
<p><strong>说明：</strong> <code>break</code>关键字来提前终止循环，需要注意的是<code>break</code>只能终止它所在的那个循环，这一点在使用嵌套的循环结构需要引起注意。除了<code>break</code>之外，还有另一个关键字是<code>continue</code>，它可以用来放弃本次循环后续的代码直接让循环进入下一轮。</p>
</blockquote>
<h2 id="函数和模块的使用"><a href="#函数和模块的使用" class="headerlink" title="函数和模块的使用"></a>函数和模块的使用</h2><p>编程大师<em>Martin Fowler</em>先生曾经说过：“<strong>代码有很多种坏味道，重复是最坏的一种！</strong>”，要写出高质量的代码首先要解决的就是重复代码的问题。</p>
<h3 id="定义函数"><a href="#定义函数" class="headerlink" title="定义函数"></a>定义函数</h3><p>在Python中可以使用<code>def</code>关键字来定义函数，和变量一样每个函数也有一个响亮的名字，而且命名规则跟变量的命名规则是一致的。在函数名后面的圆括号中可以放置传递给函数的参数，这一点和数学上的函数非常相似，程序中函数的参数就相当于是数学上说的函数的自变量，而函数执行完成后我们可以通过<code>return</code>关键字来返回一个值，这相当于数学上说的函数的因变量。</p>
<h3 id="函数的参数"><a href="#函数的参数" class="headerlink" title="函数的参数"></a>函数的参数</h3><p>函数是绝大多数编程语言中都支持的一个代码的“构建块”，但是Python中的函数与其他语言中的函数还是有很多不太相同的地方，其中一个显著的区别就是Python对函数参数的处理。在Python中，函数的参数可以有默认值，也支持使用可变参数，所以Python并不需要像其他语言一样支持<span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3dpa2kvJUU1JTg3JUJEJUU2JTk1JUIwJUU5JTg3JThEJUU4JUJEJUJE">函数的重载<i class="fa fa-external-link-alt"></i></span>，因为我们在定义一个函数的时候可以让它有多种不同的使用方式.</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 在参数名前面的*表示args是一个可变参数</span></span><br><span class="line"><span class="comment"># 即在调用add函数时可以传入0个或多个参数</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">add</span>(<span class="params">*args</span>):</span></span><br><span class="line">    total = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> val <span class="keyword">in</span> args:</span><br><span class="line">        total += val</span><br><span class="line">    <span class="keyword">return</span> total</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">print(add())</span><br><span class="line">print(add(<span class="number">1</span>))</span><br><span class="line">print(add(<span class="number">1</span>, <span class="number">2</span>))</span><br><span class="line">print(add(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>))</span><br><span class="line">print(add(<span class="number">1</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">7</span>, <span class="number">9</span>))</span><br></pre></td></tr></table></figure>

<h3 id="用模块管理函数"><a href="#用模块管理函数" class="headerlink" title="用模块管理函数"></a>用模块管理函数</h3><p>需要说明的是，如果我们导入的模块除了定义函数之外还中有可以执行代码，那么Python解释器在导入这个模块时就会执行这些代码，事实上我们可能并不希望如此，因此如果我们在模块中编写了执行代码，最好是将这些执行代码放入如下所示的条件中，这样的话除非直接运行该模块，if条件下的这些代码是不会执行的，因为只有直接执行的模块的名字才是“__main__”。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">foo</span>():</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">bar</span>():</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># __name__是Python中一个隐含的变量它代表了模块的名字</span></span><br><span class="line"><span class="comment"># 只有被Python解释器直接执行的模块的名字才是__main__</span></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    print(<span class="string">&#x27;call foo()&#x27;</span>)</span><br><span class="line">    foo()</span><br><span class="line">    print(<span class="string">&#x27;call bar()&#x27;</span>)</span><br><span class="line">    bar()</span><br></pre></td></tr></table></figure>
<h2 id="字符串和常用数据结构"><a href="#字符串和常用数据结构" class="headerlink" title="字符串和常用数据结构"></a>字符串和常用数据结构</h2><ul>
<li>sorted函数返回列表排序后的拷贝不会修改传入的列表</li>
<li>函数的设计就应该像sorted函数一样尽可能不产生副作用</li>
<li>通过<code>yield</code>关键字将一个普通函数改造成生成器函数。</li>
</ul>
<h3 id="使用元组"><a href="#使用元组" class="headerlink" title="使用元组"></a>使用元组</h3><ol>
<li>元组中的元素是无法修改的</li>
<li>元组在创建时间和占用的空间上面都优于列表。</li>
</ol>
<h2 id="面向对象编程基础"><a href="#面向对象编程基础" class="headerlink" title="面向对象编程基础"></a>面向对象编程基础</h2><p>类是抽象的概念，而对象是具体的东西。在面向对象编程的世界中，一切皆为对象，对象都有属性和行为，每个对象都是独一无二的，而且对象一定属于某个类（型）</p>
<p>在Python中可以使用<code>class</code>关键字定义类，然后在类中通过之前学习过的函数来定义方法，这样就可以将对象的动态特征描述出来，</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Student</span>(<span class="params"><span class="built_in">object</span></span>):</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># __init__是一个特殊方法用于在创建对象时进行初始化操作</span></span><br><span class="line">    <span class="comment"># 通过这个方法我们可以为学生对象绑定name和age两个属性</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self, name, age</span>):</span></span><br><span class="line">        self.name = name</span><br><span class="line">        self.age = age</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">study</span>(<span class="params">self, course_name</span>):</span></span><br><span class="line">        print(<span class="string">&#x27;%s正在学习%s.&#x27;</span> % (self.name, course_name))</span><br><span class="line"></span><br><span class="line">    <span class="comment"># PEP 8要求标识符的名字用全小写多个单词用下划线连接</span></span><br><span class="line">    <span class="comment"># 但是部分程序员和公司更倾向于使用驼峰命名法(驼峰标识)</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">watch_movie</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="keyword">if</span> self.age &lt; <span class="number">18</span>:</span><br><span class="line">            print(<span class="string">&#x27;%s只能观看《熊出没》.&#x27;</span> % self.name)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            print(<span class="string">&#x27;%s正在观看岛国爱情大电影.&#x27;</span> % self.name)</span><br></pre></td></tr></table></figure>

<h2 id="面向对象进阶"><a href="#面向对象进阶" class="headerlink" title="面向对象进阶"></a>面向对象进阶</h2><h3 id="property装饰器"><a href="#property装饰器" class="headerlink" title="@property装饰器"></a>@property装饰器</h3><p>使用@property包装器来包装getter和setter方法，使得对属性的访问既安全又方便，</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span>(<span class="params"><span class="built_in">object</span></span>):</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self, name, age</span>):</span></span><br><span class="line">        self._name = name</span><br><span class="line">        self._age = age</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 访问器 - getter方法</span></span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">name</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="keyword">return</span> self._name</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 访问器 - getter方法</span></span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">age</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="keyword">return</span> self._age</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 修改器 - setter方法</span></span><br><span class="line"><span class="meta">    @age.setter</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">age</span>(<span class="params">self, age</span>):</span></span><br><span class="line">        self._age = age</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">play</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="keyword">if</span> self._age &lt;= <span class="number">16</span>:</span><br><span class="line">            print(<span class="string">&#x27;%s正在玩飞行棋.&#x27;</span> % self._name)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            print(<span class="string">&#x27;%s正在玩斗地主.&#x27;</span> % self._name)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">main</span>():</span></span><br><span class="line">    person = Person(<span class="string">&#x27;王大锤&#x27;</span>, <span class="number">12</span>)</span><br><span class="line">    person.play()</span><br><span class="line">    person.age = <span class="number">22</span></span><br><span class="line">    person.play()</span><br><span class="line">    <span class="comment"># person.name = &#x27;白元芳&#x27;  # AttributeError: can&#x27;t set attribute</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure>

<h3 id="slots-魔法"><a href="#slots-魔法" class="headerlink" title="__slots__魔法"></a>__slots__魔法</h3><p>通常，动态语言允许我们在程序运行时给对象绑定新的属性或方法，当然也可以对已经绑定的属性和方法进行解绑定。但是如果我们需要限定自定义类型的对象只能绑定某些属性，可以通过在类中定义__slots__变量来进行限定。需要注意的是__slots__的限定只对当前类的对象生效，对子类并不起任何作用。</p>
<ul>
<li><h1 id="限定Person对象只能绑定-name-age和-gender属性"><a href="#限定Person对象只能绑定-name-age和-gender属性" class="headerlink" title="限定Person对象只能绑定_name, _age和_gender属性"></a>限定Person对象只能绑定_name, _age和_gender属性</h1><ul>
<li>__slots__ = (‘_name’, ‘_age’, ‘_gender’)</li>
</ul>
</li>
</ul>
<h3 id="静态方法和类方法"><a href="#静态方法和类方法" class="headerlink" title="静态方法和类方法"></a>静态方法和类方法</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@staticmethod</span></span><br><span class="line">   <span class="function"><span class="keyword">def</span> <span class="title">is_valid</span>(<span class="params">a, b, c</span>):</span></span><br><span class="line">       <span class="keyword">return</span> a + b &gt; c <span class="keyword">and</span> b + c &gt; a <span class="keyword">and</span> a + c &gt; b</span><br></pre></td></tr></table></figure>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@classmethod</span></span><br><span class="line">   <span class="function"><span class="keyword">def</span> <span class="title">now</span>(<span class="params">cls</span>):</span></span><br><span class="line">       ctime = localtime(time())</span><br><span class="line">       <span class="keyword">return</span> cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec)</span><br></pre></td></tr></table></figure>

<h3 id="类之间的关系"><a href="#类之间的关系" class="headerlink" title="类之间的关系"></a>类之间的关系</h3><p>简单的说，类和类之间的关系有三种：is-a、has-a和use-a关系。</p>
<h3 id="继承和多态"><a href="#继承和多态" class="headerlink" title="继承和多态"></a>继承和多态</h3>
      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://wangkejie.gitee.io/blogwebpage/iOS/origincode/category.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blogwebpage/images/avatar.gif">
      <meta itemprop="name" content="Jack Wang">
      <meta itemprop="description" content="书山有路勤为径，学海无涯苦作舟！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="王科杰">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/blogwebpage/iOS/origincode/category.html" class="post-title-link" itemprop="url">Objective-C中的分类</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-10-10 11:26:39" itemprop="dateCreated datePublished" datetime="2017-10-10T11:26:39+08:00">2017-10-10</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/" itemprop="url" rel="index"><span itemprop="name">源码学习</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/%E5%88%86%E7%B1%BB%E5%8E%9F%E7%90%86/" itemprop="url" rel="index"><span itemprop="name">分类原理</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>面试题</p>
<ul>
<li>Category的实现原理，以及Category为什么只能加方法不能加属性。</li>
<li>Category中有load方法吗？load方法是什么时候调用的？load 方法能继承吗？</li>
<li>load、initialize的区别，以及它们在category重写的时候的调用的次序。</li>
</ul>
<p>分类中的对象方法依然是存储在类对象中的，同对象方法在同一个地方，那么调用步骤也同调用对象方法一样。如果是类方法的话，也同样是存储在元类对象中</p>
<p>category和extension?</p>
<ul>
<li><p>extension在编译期决议，它就是类的一部分，在编译期和头文件里的@interface以及实现文件里的@implement一起形成一个完整的类，它伴随类的产生而产生，亦随之一起消亡。extension一般用来隐藏类的私有信息，你必须有一个类的源码才能为一个类添加extension，所以你无法为系统的类比如NSString添加extension。<span class="exturl" data-url="aHR0cHM6Ly9kZXZlbG9wZXIuYXBwbGUuY29tL2xpYnJhcnkvYXJjaGl2ZS9kb2N1bWVudGF0aW9uL0NvY29hL0NvbmNlcHR1YWwvUHJvZ3JhbW1pbmdXaXRoT2JqZWN0aXZlQy9DdXN0b21pemluZ0V4aXN0aW5nQ2xhc3Nlcy9DdXN0b21pemluZ0V4aXN0aW5nQ2xhc3Nlcy5odG1s">（详见2）<i class="fa fa-external-link-alt"></i></span></p>
</li>
<li><p>但是category则完全不一样，它是在运行期决议的。 就category和extension的区别来看，我们可以推导出一个明显的事实，extension可以添加实例变量，而category是无法添加实例变量的（因为在运行期，对象的内存布局已经确定，如果添加实例变量就会破坏类的内部布局，这对编译型语言来说是灾难性的）。</p>
</li>
</ul>
<h1 id="话不多说，源码来看"><a href="#话不多说，源码来看" class="headerlink" title="话不多说，源码来看"></a>话不多说，源码来看</h1><p>我们知道，所有的OC类和对象，在runtime层都是用struct表示的，category也不例外，在runtime层，category用结构体category_t（在objc-runtime-new.h中可以找到此定义），它包含了：</p>
<ul>
<li>1)、类的名字（name）</li>
<li>2)、类（cls）</li>
<li>3)、category中所有给类添加的实例方法的列表（instanceMethods）</li>
<li>4)、category中所有添加的类方法的列表（classMethods）</li>
<li>5)、category实现的所有协议的列表（protocols）</li>
<li>6)、category中添加的所有属性（instanceProperties）</li>
</ul>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> category_t &#123;</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">char</span> *name;</span><br><span class="line">    classref_t cls;</span><br><span class="line">    <span class="keyword">struct</span> method_list_t *instanceMethods; <span class="comment">// 对象方法</span></span><br><span class="line">    <span class="keyword">struct</span> method_list_t *classMethods; <span class="comment">// 类方法</span></span><br><span class="line">    <span class="keyword">struct</span> protocol_list_t *protocols; <span class="comment">// 协议</span></span><br><span class="line">    <span class="keyword">struct</span> property_list_t *instanceProperties; <span class="comment">// 属性</span></span><br><span class="line">    <span class="comment">// Fields below this point are not always present on disk.</span></span><br><span class="line">    <span class="keyword">struct</span> property_list_t *_classProperties;</span><br><span class="line"></span><br><span class="line">    method_list_t *methodsForMeta(<span class="keyword">bool</span> isMeta) &#123;</span><br><span class="line">        <span class="keyword">if</span> (isMeta) <span class="keyword">return</span> classMethods;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">return</span> instanceMethods;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    property_list_t *propertiesForMeta(<span class="keyword">bool</span> isMeta, <span class="keyword">struct</span> header_info *hi);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>从源码基本可以看出我们平时使用categroy的方式，对象方法，类方法，协议，和属性都可以找到对应的存储方式。并且我们发现分类结构体中是不存在成员变量的，因此分类中是不允许添加成员变量的。分类中添加的属性并不会帮助我们自动生成成员变量，只会生成get set方法的声明，需要我们自己去实现。</p>
<p>通过源码我们发现，分类的方法，协议，属性等好像确实是存放在categroy结构体里面的，那么他又是如何存储在类对象中的呢？<br>我们来看一下底层的内部方法探寻其中的原理。<br>首先我们通过命令行将MyClass+MyAddition.m.m文件转化为c++文件，查看其中的编译过程。</p>
<figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc MyClass+<span class="module-access"><span class="module"><span class="identifier">MyAddition</span>.</span></span>m</span><br></pre></td></tr></table></figure>
<p>好吧，我们得到了一个3M大小，10w多行的.cpp文件（这绝对是Apple值得吐槽的一点），我们忽略掉所有和我们无关的东西，在文件的最后，我们找到了如下代码片段：</p>
<figure class="highlight sqf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">static struct <span class="comment">/*_method_list_t*/</span> &#123;</span><br><span class="line">unsigned int entsize;  <span class="comment">// sizeof(struct _objc_method)</span></span><br><span class="line">unsigned int method_count;</span><br><span class="line">struct <span class="variable">_objc_method</span> method_list[<span class="number">1</span>];</span><br><span class="line">&#125; <span class="variable">_OBJC_</span>$<span class="variable">_CATEGORY_INSTANCE_METHODS_MyClass_</span>$<span class="variable">_MyAddition</span> <span class="variable">__attribute__</span> ((used, section (<span class="string">&quot;__DATA,__objc_const&quot;</span>))) = &#123;</span><br><span class="line"><span class="built_in">sizeof</span>(<span class="variable">_objc_method</span>),</span><br><span class="line"><span class="number">1</span>,</span><br><span class="line">&#123;&#123;(struct objc_selector *)<span class="string">&quot;printName&quot;</span>, <span class="string">&quot;v16@0:8&quot;</span>, (void *)<span class="variable">_I_MyClass_MyAddition_printName</span>&#125;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">static struct <span class="comment">/*_prop_list_t*/</span> &#123;</span><br><span class="line">unsigned int entsize;  <span class="comment">// sizeof(struct _prop_t)</span></span><br><span class="line">unsigned int count_of_properties;</span><br><span class="line">struct <span class="variable">_prop_t</span> prop_list[<span class="number">1</span>];</span><br><span class="line">&#125; <span class="variable">_OBJC_</span>$<span class="variable">_PROP_LIST_MyClass_</span>$<span class="variable">_MyAddition</span> <span class="variable">__attribute__</span> ((used, section (<span class="string">&quot;__DATA,__objc_const&quot;</span>))) = &#123;</span><br><span class="line"><span class="built_in">sizeof</span>(<span class="variable">_prop_t</span>),</span><br><span class="line"><span class="number">1</span>,</span><br><span class="line">&#123;&#123;<span class="string">&quot;name&quot;</span>,<span class="string">&quot;T@\&quot;</span>NSString\<span class="string">&quot;,C,N&quot;</span>&#125;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">extern <span class="string">&quot;C&quot;</span> <span class="variable">__declspec</span>(dllexport) struct <span class="variable">_class_t</span> OBJC_CLASS_$<span class="variable">_MyClass</span>;</span><br><span class="line"></span><br><span class="line">static struct <span class="variable">_category_t</span> <span class="variable">_OBJC_</span>$<span class="variable">_CATEGORY_MyClass_</span>$<span class="variable">_MyAddition</span> <span class="variable">__attribute__</span> ((used, section (<span class="string">&quot;__DATA,__objc_const&quot;</span>))) =</span><br><span class="line">&#123;</span><br><span class="line"><span class="string">&quot;MyClass&quot;</span>,</span><br><span class="line"><span class="number">0</span>, <span class="comment">// &amp;OBJC_CLASS_$_MyClass,</span></span><br><span class="line">(const struct <span class="variable">_method_list_t</span> *)&amp;<span class="variable">_OBJC_</span>$<span class="variable">_CATEGORY_INSTANCE_METHODS_MyClass_</span>$<span class="variable">_MyAddition</span>,</span><br><span class="line"><span class="number">0</span>,</span><br><span class="line"><span class="number">0</span>,</span><br><span class="line">(const struct <span class="variable">_prop_list_t</span> *)&amp;<span class="variable">_OBJC_</span>$<span class="variable">_PROP_LIST_MyClass_</span>$<span class="variable">_MyAddition</span>,</span><br><span class="line">&#125;;</span><br><span class="line">static void OBJC_CATEGORY_SETUP_$<span class="variable">_MyClass_</span>$<span class="variable">_MyAddition</span>(void ) &#123;</span><br><span class="line"><span class="variable">_OBJC_</span>$<span class="variable">_CATEGORY_MyClass_</span>$<span class="variable">_MyAddition</span>.cls = &amp;OBJC_CLASS_$<span class="variable">_MyClass</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">#pragma section(<span class="meta-string">&quot;.objc_inithooks$B&quot;</span>, long, read, write)</span></span><br><span class="line"><span class="variable">__declspec</span>(allocate(<span class="string">&quot;.objc_inithooks$B&quot;</span>)) static void *OBJC_CATEGORY_SETUP[] = &#123;</span><br><span class="line">(void *)&amp;OBJC_CATEGORY_SETUP_$<span class="variable">_MyClass_</span>$<span class="variable">_MyAddition</span>,</span><br><span class="line">&#125;;</span><br><span class="line">static struct <span class="variable">_class_t</span> *L_OBJC_LABEL_CLASS_$ [<span class="number">1</span>] <span class="variable">__attribute__</span>((used, section (<span class="string">&quot;__DATA, __objc_classlist,regular,no_dead_strip&quot;</span>)))= &#123;</span><br><span class="line">&amp;OBJC_CLASS_$<span class="variable">_MyClass</span>,</span><br><span class="line">&#125;;</span><br><span class="line">static struct <span class="variable">_class_t</span> *<span class="variable">_OBJC_LABEL_NONLAZY_CLASS_</span>$[] = &#123;</span><br><span class="line">&amp;OBJC_CLASS_$<span class="variable">_MyClass</span>,</span><br><span class="line">&#125;;</span><br><span class="line">static struct <span class="variable">_category_t</span> *L_OBJC_LABEL_CATEGORY_$ [<span class="number">1</span>] <span class="variable">__attribute__</span>((used, section (<span class="string">&quot;__DATA, __objc_catlist,regular,no_dead_strip&quot;</span>)))= &#123;</span><br><span class="line">&amp;<span class="variable">_OBJC_</span>$<span class="variable">_CATEGORY_MyClass_</span>$<span class="variable">_MyAddition</span>,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>我们可以看到，</p>
<ul>
<li><p>1)、首先编译器生成了实例方法列表OBJC$_CATEGORY_INSTANCE_METHODSMyClass$_MyAddition和属性列表OBJC$_PROP_LISTMyClass$_MyAddition，两者的命名都遵循了公共前缀+类名+category名字的命名方式，而且实例方法列表里面填充的正是我们在MyAddition这个category里面写的方法printName，而属性列表里面填充的也正是我们在MyAddition里添加的name属性。还有一个需要注意到的事实就是category的名字用来给各种列表以及后面的category结构体本身命名，而且有static来修饰，所以在同一个编译单元里我们的<code>category名不能重复</code>，否则会出现编译错误。</p>
</li>
<li><p>2)、其次，编译器生成了category本身OBJC$_CATEGORYMyClass$_MyAddition，并用前面生成的列表来初始化category本身。</p>
</li>
<li><p>3)、最后，编译器在DATA段下的objc_catlist section里保存了一个大小为1的category_t的数组L_OBJC_LABELCATEGORY$（当然，如果有多个category，会生成对应长度的数组^_^），用于运行期category的加载。</p>
</li>
</ul>
<p>到这里，编译器的工作就接近尾声了，对于category在运行期怎么加载?</p>
<h1 id="Runtime的时候加载"><a href="#Runtime的时候加载" class="headerlink" title="Runtime的时候加载"></a>Runtime的时候加载</h1><p>我们知道，Objective-C的运行是依赖OC的runtime的，而OC的runtime和其他系统库一样，是OS X和iOS通过dyld动态加载的。</p>
<p>对于OC运行时，入口方法如下（在objc-os.mm文件中）：</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> _objc_init(<span class="keyword">void</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">bool</span> initialized = <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">if</span> (initialized) <span class="keyword">return</span>;</span><br><span class="line">    initialized = <span class="literal">true</span>;</span><br><span class="line">   </span><br><span class="line">    <span class="comment">// fixme defer initialization until an objc-using image is found?</span></span><br><span class="line">    environ_init();</span><br><span class="line">    tls_init();</span><br><span class="line">    lock_init();</span><br><span class="line">    exception_init();</span><br><span class="line">       </span><br><span class="line">    <span class="comment">// Register for unmap first, in case some +load unmaps something</span></span><br><span class="line">    _dyld_register_func_for_remove_image(&amp;unmap_image);</span><br><span class="line">    dyld_register_image_state_change_handler(dyld_image_state_bound,</span><br><span class="line">                                             <span class="number">1</span><span class="comment">/*batch*/</span>, &amp;map_images);</span><br><span class="line">    dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, <span class="number">0</span><span class="comment">/*not batch*/</span>, &amp;load_images);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>ategory被附加到类上面是在map_images的时候发生的，在new-ABI的标准下，_objc_init里面的调用的map_images最终会调用objc-runtime-new.mm里面的_read_images方法，而在_read_images方法的结尾，有以下的代码片段：</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Discover categories. </span></span><br><span class="line">    <span class="keyword">for</span> (EACH_HEADER) &#123;</span><br><span class="line">        category_t **catlist =</span><br><span class="line">            _getObjc2CategoryList(hi, &amp;count);</span><br><span class="line">        <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; count; i++) &#123;</span><br><span class="line">            category_t *cat = catlist[i];</span><br><span class="line">            class_t *cls = remapClass(cat-&gt;cls);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (!cls) &#123;</span><br><span class="line">                <span class="comment">// Category&#x27;s target class is missing (probably weak-linked).</span></span><br><span class="line">                <span class="comment">// Disavow any knowledge of this category.</span></span><br><span class="line">                catlist[i] = <span class="literal">NULL</span>;</span><br><span class="line">                <span class="keyword">if</span> (PrintConnecting) &#123;</span><br><span class="line">                    _objc_inform(<span class="string">&quot;CLASS: IGNORING category \?\?\?(%s) %p with &quot;</span></span><br><span class="line">                                 <span class="string">&quot;missing weak-linked target class&quot;</span>,</span><br><span class="line">                                 cat-&gt;name, cat);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// Process this category. </span></span><br><span class="line">            <span class="comment">// First, register the category with its target class. </span></span><br><span class="line">            <span class="comment">// Then, rebuild the class&#x27;s method lists (etc) if </span></span><br><span class="line">            <span class="comment">// the class is realized. </span></span><br><span class="line">            <span class="built_in">BOOL</span> classExists = <span class="literal">NO</span>;</span><br><span class="line">            <span class="keyword">if</span> (cat-&gt;instanceMethods ||  cat-&gt;protocols </span><br><span class="line">                ||  cat-&gt;instanceProperties)</span><br><span class="line">            &#123;</span><br><span class="line">                addUnattachedCategoryForClass(cat, cls, hi);</span><br><span class="line">                <span class="keyword">if</span> (isRealized(cls)) &#123;</span><br><span class="line">                    remethodizeClass(cls);</span><br><span class="line">                    classExists = <span class="literal">YES</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (PrintConnecting) &#123;</span><br><span class="line">                    _objc_inform(<span class="string">&quot;CLASS: found category -%s(%s) %s&quot;</span>,</span><br><span class="line">                                 getName(cls), cat-&gt;name,</span><br><span class="line">                                 classExists ? <span class="string">&quot;on existing class&quot;</span> : <span class="string">&quot;&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (cat-&gt;classMethods  ||  cat-&gt;protocols </span><br><span class="line">                <span class="comment">/* ||  cat-&gt;classProperties */</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                addUnattachedCategoryForClass(cat, cls-&gt;isa, hi);</span><br><span class="line">                <span class="keyword">if</span> (isRealized(cls-&gt;isa)) &#123;</span><br><span class="line">                    remethodizeClass(cls-&gt;isa);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (PrintConnecting) &#123;</span><br><span class="line">                    _objc_inform(<span class="string">&quot;CLASS: found category +%s(%s)&quot;</span>,</span><br><span class="line">                                 getName(cls), cat-&gt;name);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>从上述代码中我们可以知道这段代码是用来查找有没有分类的。通过_getObjc2CategoryList函数获取到分类列表之后，进行遍历，获取其中的方法，协议，属性等。可以看到最终都调用了remethodizeClass(cls);函数。我们来到remethodizeClass(cls);函数内部查看。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">void</span> remethodizeClass(Class cls)</span><br><span class="line">&#123;</span><br><span class="line">    category_list *cats;</span><br><span class="line">    <span class="keyword">bool</span> isMeta;</span><br><span class="line"></span><br><span class="line">    runtimeLock.assertLocked();</span><br><span class="line"></span><br><span class="line">    isMeta = cls-&gt;isMetaClass();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Re-methodizing: check for more categories</span></span><br><span class="line">    <span class="keyword">if</span> ((cats = unattachedCategoriesForClass(cls, <span class="literal">false</span><span class="comment">/*not realizing*/</span>))) &#123;</span><br><span class="line">        <span class="keyword">if</span> (PrintConnecting) &#123;</span><br><span class="line">            _objc_inform(<span class="string">&quot;CLASS: attaching categories to class &#x27;%s&#x27; %s&quot;</span>, </span><br><span class="line">                         cls-&gt;nameForLogging(), isMeta ? <span class="string">&quot;(meta)&quot;</span> : <span class="string">&quot;&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        attachCategories(cls, cats, <span class="literal">true</span> <span class="comment">/*flush caches*/</span>);        </span><br><span class="line">        free(cats);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>通过上述代码我们发现attachCategories函数接收了类对象cls和分类数组cats，如我们一开始写的代码所示，一个类可以有多个分类。之前我们说到分类信息存储在category_t结构体中，那么多个分类则保存在category_list中。<br>我们来到attachCategories函数内部。</p>
<img src="/blogwebpage/images/iOS/attachCategoryMethods.jpeg" class="" title="attachCategoryMethods">

<p>attachCategoryMethods做的工作相对比较简单，它只是把所有category的实例方法列表拼成了一个大的实例方法列表，然后转交给了attachMethodLists方法,继续往下看</p>
<img src="/blogwebpage/images/iOS/attachLists.jpeg" class="" title="attachLists">
<p>需要注意的有两点：</p>
<ul>
<li><p>1)、category的方法没有“完全替换掉”原来类已经有的方法，也就是说如果category和原来类都有methodA，那么category附加完成之后，类的方法列表里会有两个methodA</p>
</li>
<li><p>2)、category的方法被放到了新方法列表的前面，而原来类的方法被放到了新方法列表的后面，这也就是我们平常所说的category的方法会“覆盖”掉原来类的同名方法，这是因为运行时在查找方法的时候是顺着方法列表的顺序查找的，它只要一找到对应名字的方法，就会罢休^_^，殊不知后面可能还有一样名字的方法。</p>
</li>
</ul>
<p>问： Category的实现原理，以及Category为什么只能加方法不能加属性?<br>答：分类的实现原理是将category中的方法，属性，协议数据放在category_t结构体中，然后将结构体内的方法列表拷贝到类对象的方法列表中。<br>Category可以添加属性，但是并不会自动生成成员变量及set/get方法。因为category_t结构体中并不存在成员变量。通过之前对对象的分析我们知道<code>成员变量是存放在实例对象中的</code>，并且<code>编译的那一刻</code>就已经<code>决定</code>好了。而分类是在运行时才去加载的。那么我们就无法再程序运行时将分类的成员变量中添加到实例对象的结构体中。因此分类中不可以添加成员变量。</p>
<h1 id="load-和-initialize"><a href="#load-和-initialize" class="headerlink" title="load 和 initialize"></a>load 和 initialize</h1><p>load方法会在程序启动就会调用，当装载类信息的时候就会调用。 调用顺序看一下源代码。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> call_load_methods(<span class="keyword">void</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">bool</span> loading = <span class="literal">NO</span>;</span><br><span class="line">    <span class="keyword">bool</span> more_categories;</span><br><span class="line"></span><br><span class="line">    loadMethodLock.assertLocked();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Re-entrant calls do nothing; the outermost call will finish the job.</span></span><br><span class="line">    <span class="keyword">if</span> (loading) <span class="keyword">return</span>;</span><br><span class="line">    loading = <span class="literal">YES</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">void</span> *pool = objc_autoreleasePoolPush();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">do</span> &#123;</span><br><span class="line">        <span class="comment">// 1. Repeatedly call class +loads until there aren&#x27;t any more</span></span><br><span class="line">        <span class="keyword">while</span> (loadable_classes_used &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            call_class_loads();   <span class="comment">//看这里看这里！！！！！ 先调用类的load</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 2. Call category +loads ONCE</span></span><br><span class="line">        more_categories = call_category_loads();  <span class="comment">//看这里看这里！！！！！ 分类的load</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 3. Run more +loads if there are classes OR more untried categories</span></span><br><span class="line">    &#125; <span class="keyword">while</span> (loadable_classes_used &gt; <span class="number">0</span>  ||  more_categories);</span><br><span class="line"></span><br><span class="line">    objc_autoreleasePoolPop(pool);</span><br><span class="line"></span><br><span class="line">    loading = <span class="literal">NO</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>load方法依次调用 类 分类  ,函数式调用的哦</p>
<p>initialize是通过消息发送机制调用的，消息发送机制通过isa指针找到对应的方法与实现，因此先找到分类方法中的实现，会优先调用分类方法中的实现。</p>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>问：Category中有load方法吗？load方法是什么时候调用的？load 方法能继承吗？<br>答：Category中有load方法，load方法在程序<code>启动装载类信息</code>(_objc_init)的时候就会调用。load方法可以继承。调用子类的load方法之前，会先调用<code>父类的load</code>方法<br>问：load、initialize的区别，以及它们在category重写的时候的调用的次序。<br>答：区别在于调用方式和调用时刻<br>调用方式：load是根据函数地址直接调用，initialize是通过objc_msgSend调用<br>调用时刻：load是runtime加载类、分类的时候调用（只会调用1次），initialize是类第一次接收到消息的时候调用，每一个类只会initialize一次（父类的initialize方法可能会被调用多次）<br>调用顺序：先调用类的load方法，先编译那个类，就先调用load。在调用load之前会先调用父类的load方法。分类中load方法不会覆盖本类的load方法，先编译的分类优先调用load方法。initialize先初始化父类，之后再初始化子类。如果子类没有实现+initialize，会调用父类的+initialize（所以父类的+initialize可能会被调用多次），如果分类实现了+initialize，就覆盖类本身的+initialize调用。</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://wangkejie.gitee.io/blogwebpage/iOS/origincode/dispatch_once.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blogwebpage/images/avatar.gif">
      <meta itemprop="name" content="Jack Wang">
      <meta itemprop="description" content="书山有路勤为径，学海无涯苦作舟！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="王科杰">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/blogwebpage/iOS/origincode/dispatch_once.html" class="post-title-link" itemprop="url">dispatch_once</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-10-10 11:26:39" itemprop="dateCreated datePublished" datetime="2017-10-10T11:26:39+08:00">2017-10-10</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/" itemprop="url" rel="index"><span itemprop="name">源码学习</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/dispatch-once/" itemprop="url" rel="index"><span itemprop="name">dispatch_once</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <p>本文使用的源码是<span class="exturl" data-url="aHR0cHM6Ly9vcGVuc291cmNlLmFwcGxlLmNvbS90YXJiYWxscy9saWJkaXNwYXRjaC8=">libdispatch-1008.220.2<i class="fa fa-external-link-alt"></i></span>版本。</p>
<h1 id="once-h"><a href="#once-h" class="headerlink" title="once.h"></a>once.h</h1><figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//第一部分 dispatch_once 的定义，其实是一个_dispatch_once</span></span><br><span class="line"><span class="keyword">void</span></span><br><span class="line"><span class="built_in">dispatch_once</span>(<span class="built_in">dispatch_once_t</span> *predicate,</span><br><span class="line">		DISPATCH_NOESCAPE dispatch_block_t block);</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span></span><br><span class="line">_<span class="built_in">dispatch_once</span>(<span class="built_in">dispatch_once_t</span> *predicate,</span><br><span class="line">		DISPATCH_NOESCAPE dispatch_block_t block)</span><br><span class="line">&#123;</span><br><span class="line">	<span class="keyword">if</span> (DISPATCH_EXPECT(*predicate, ~<span class="number">0</span>l) != ~<span class="number">0</span>l) &#123;</span><br><span class="line">		<span class="built_in">dispatch_once</span>(predicate, block);</span><br><span class="line">	&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">		dispatch_compiler_barrier();</span><br><span class="line">	&#125;</span><br><span class="line">	DISPATCH_COMPILER_CAN_ASSUME(*predicate == ~<span class="number">0</span>l);</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">undef</span> dispatch_once</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> dispatch_once _dispatch_once</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//第二部分 dispatch_once_f 的定义，其实是一个 ——dispatch_once_f</span></span><br><span class="line"><span class="keyword">void</span></span><br><span class="line">dispatch_once_f(<span class="built_in">dispatch_once_t</span> *predicate, <span class="keyword">void</span> *_Nullable context,</span><br><span class="line">		dispatch_function_t function);</span><br><span class="line"><span class="keyword">void</span></span><br><span class="line">_dispatch_once_f(<span class="built_in">dispatch_once_t</span> *predicate, <span class="keyword">void</span> *_Nullable context,</span><br><span class="line">		dispatch_function_t function)</span><br><span class="line">&#123;</span><br><span class="line">	<span class="keyword">if</span> (DISPATCH_EXPECT(*predicate, ~<span class="number">0</span>l) != ~<span class="number">0</span>l) &#123;</span><br><span class="line">		dispatch_once_f(predicate, context, function);</span><br><span class="line">	&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">		dispatch_compiler_barrier();</span><br><span class="line">	&#125;</span><br><span class="line">	DISPATCH_COMPILER_CAN_ASSUME(*predicate == ~<span class="number">0</span>l);</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">undef</span> dispatch_once_f</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> dispatch_once_f _dispatch_once_f</span></span><br></pre></td></tr></table></figure>

<p>由上可以简单的知道，dispatch_once 调用了 dispatch_once_f方法，<br>为了解释<code>DISPATCH_EXPECT</code>,我把宏定义贴下面</p>
<figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-id">#if</span> <span class="selector-tag">__GNUC__</span></span><br><span class="line"><span class="selector-id">#define</span> <span class="selector-tag">DISPATCH_EXPECT</span>(x, v) <span class="selector-tag">__builtin_expect</span>((x), (v))</span><br><span class="line"><span class="selector-id">#define</span> <span class="selector-tag">dispatch_compiler_barrier</span>()  <span class="selector-tag">__asm__</span> <span class="selector-tag">__volatile__</span>(<span class="string">&quot;&quot;</span> ::: <span class="string">&quot;memory&quot;</span>)</span><br><span class="line"><span class="selector-id">#else</span></span><br><span class="line"><span class="selector-id">#define</span> <span class="selector-tag">DISPATCH_EXPECT</span>(x, v) (x)</span><br><span class="line"><span class="selector-id">#define</span> <span class="selector-tag">dispatch_compiler_barrier</span>()  <span class="selector-tag">do</span> &#123; &#125; <span class="selector-tag">while</span> (<span class="number">0</span>)</span><br><span class="line"><span class="selector-id">#endif</span></span><br></pre></td></tr></table></figure>

<blockquote>
<p>头文件中用到一个DISPATCH_EXPECT宏，这个宏展开之后其实是一个GCC内置函数__builtin_expect(exp, c)，在GCC手册上是这样描述这个函数的：由于大部分程序员在分支预测方面做得很糟糕，所以 GCC 提供了这个内建函数来帮助程序员处理分支预测，优化程序。其第一个参数 exp 为一个整型表达式，这个内建函数的返回值也是这个 exp ，而 c 为一个编译期常量。这个函数的语义是：你期望 exp 表达式的值等于常量 c ，从而 GCC 为你优化程序，将符合这个条件的分支放在合适的地方</p>
</blockquote>
<p>我们来看实现</p>
<h1 id="once-c"><a href="#once-c" class="headerlink" title="once.c"></a>once.c</h1>
      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://wangkejie.gitee.io/blogwebpage/iOS/origincode/metaclass.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blogwebpage/images/avatar.gif">
      <meta itemprop="name" content="Jack Wang">
      <meta itemprop="description" content="书山有路勤为径，学海无涯苦作舟！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="王科杰">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/blogwebpage/iOS/origincode/metaclass.html" class="post-title-link" itemprop="url">Objective-C中的元类是什么？</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-10-10 11:26:39" itemprop="dateCreated datePublished" datetime="2017-10-10T11:26:39+08:00">2017-10-10</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/" itemprop="url" rel="index"><span itemprop="name">源码学习</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/%E5%85%83%E7%B1%BB%E6%98%AF%E4%BB%80%E4%B9%88/" itemprop="url" rel="index"><span itemprop="name">元类是什么</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h1 id="在运行时创建一个类"><a href="#在运行时创建一个类" class="headerlink" title="在运行时创建一个类"></a>在运行时创建一个类</h1><figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Class newClass = objc_allocateClassPair([<span class="built_in">NSError</span> <span class="keyword">class</span>], <span class="string">&quot;RuntimeErrorSubclass&quot;</span>, <span class="number">0</span>);</span><br><span class="line">class_addMethod(newClass, <span class="keyword">@selector</span>(report), (IMP)ReportFunction, <span class="string">&quot;v@:&quot;</span>);</span><br><span class="line">objc_registerClassPair(newClass);</span><br></pre></td></tr></table></figure>

<p>添加的方法使用名为ReportFunction其实现的函数，其定义如下：</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> ReportFunction(<span class="keyword">id</span> <span class="keyword">self</span>, SEL _cmd)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@&quot;This object is %p.&quot;</span>, <span class="keyword">self</span>);</span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@&quot;Class is %@, and super is %@.&quot;</span>, [<span class="keyword">self</span> <span class="keyword">class</span>], [<span class="keyword">self</span> superclass]);</span><br><span class="line">    </span><br><span class="line">    Class currentClass = [<span class="keyword">self</span> <span class="keyword">class</span>];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt; <span class="number">5</span>; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">NSLog</span>(<span class="string">@&quot;Following the isa pointer %d times gives %p&quot;</span>, i, currentClass);</span><br><span class="line">        currentClass = object_getClass(currentClass);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@&quot;NSObject&#x27;s class is %p&quot;</span>, [<span class="built_in">NSObject</span> <span class="keyword">class</span>]);</span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@&quot;NSObject&#x27;s meta class is %p&quot;</span>, object_getClass([<span class="built_in">NSObject</span> <span class="keyword">class</span>]));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>从表面上看，这一切都非常简单。在运行时创建类只需三个简单的步骤：</p>
<ul>
<li>为“类对”分配存储（使用objc_allocateClassPair）。</li>
<li>根据需要将方法和ivars添加到类中（我添加了一个方法class_addMethod）。</li>
<li>注册该类以便可以使用（使用objc_registerClassPair）。</li>
</ul>
<p>然而，当前的问题是：什么是“class pair”？该函数objc_allocateClassPair只返回一个值：类。那对的另一半在哪里？</p>
<p>我敢肯定你已经猜到了这一对的另一半是元类（这是这篇文章的标题）但是为了解释这是什么以及你为什么需要它，我将给出一些关于对象的背景知识和Objective-C中的类。</p>
<h1 id="成为对象de数据结构需要什么？"><a href="#成为对象de数据结构需要什么？" class="headerlink" title="成为对象de数据结构需要什么？"></a>成为对象de数据结构需要什么？</h1><p>在Objective-C中，对象的类由其isa指针确定。该isa指针指向对象的类。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> objc_object &#123;</span><br><span class="line">    Class isa;</span><br><span class="line">&#125; *<span class="keyword">id</span>;</span><br></pre></td></tr></table></figure>
<p>这就是说：任何以指向Class结构的指针开头的结构都可以视为objc_object。</p>
<p>Objective-C中对象最重要的特性是你可以向它们发送消息：</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[<span class="string">@&quot;stringValue&quot;</span></span><br><span class="line">    writeToFile:<span class="string">@&quot;/file.txt&quot;</span> atomically:<span class="literal">YES</span> encoding:<span class="built_in">NSUTF8StringEncoding</span> error:<span class="literal">NULL</span>];</span><br></pre></td></tr></table></figure>

<h1 id="什么是元级？"><a href="#什么是元级？" class="headerlink" title="什么是元级？"></a>什么是元级？</h1><p>现在，正如您可能已经知道的那样，Class 在Objective-C中也是一个对象。这意味着您可以发送消息给Class。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">NSStringEncoding</span> defaultStringEncoding = [<span class="built_in">NSString</span> defaultStringEncoding];</span><br></pre></td></tr></table></figure>
<p>在这种情况下，defaultStringEncoding被发送到NSString class。</p>
<p>这是有效的，因为Class 在Objective-C中的每个都是一个对象本身。这意味着Class结构必须以isa指针开头，以便它与objc_object上面显示的结构二进制兼容，并且结构中的下一个字段必须是指向superclass（或nil基类）的指针。</p>
<p>正如我上面所展示的那样Class，根据您运行的运行时版本，有几种不同的方式可以定义，但是，它们都以isa字段后跟字段开头superclass。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> objc_class *Class;</span><br><span class="line"><span class="keyword">struct</span> objc_class &#123;</span><br><span class="line">    Class isa;</span><br><span class="line">    Class super_class;</span><br><span class="line">    <span class="comment">/* followed by runtime specific details... */</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>但是，为了让我们调用类上的方法，类的isa指针本身必须指向一个类结构，而这个类结构必须包含我们可以在类上调用的方法列表。</p>
<p>这就引出了元类的定义:元类是类对象的类。</p>
<p>简单地说:</p>
<ul>
<li>当您向对象发送消息时，将在对象类的方法列表中查找该消息。</li>
<li>当您向类发送消息时，将在类的元类的方法列表中查找该消息。</li>
</ul>
<p>元类非常重要，因为它存储类的类方法。每个类必须有一个惟一的元类，因为每个类都有一个潜在惟一的类方法列表。</p>
<h1 id="元类的类是什么"><a href="#元类的类是什么" class="headerlink" title="元类的类是什么?"></a>元类的类是什么?</h1><p>与之前的类一样，元类也是一个对象。这意味着您也可以在其上调用方法。当然，这意味着它还必须有一个类。</p>
<p>所有元类都使用<code>基类的元类</code>(继承层次结构中顶层类的元类)作为<code>类</code>。这意味着对于从NSObject派生的所有类(大多数类)，<code>元类</code>都将<code>NSObject元类</code>作为其<code>类</code>。</p>
<p>遵循所有元类都使用基类的元类作为它们的类的规则，任何基元类都将是它自己的类(它们的isa指针指向它们自己)。这意味着NSObject元类上的isa指针指向它自己(它是它自己的一个实例)。</p>
<h1 id="类和元类的继承"><a href="#类和元类的继承" class="headerlink" title="类和元类的继承"></a>类和元类的继承</h1><p>就像类用它的超类指针指向超类一样，元类使用它自己的超类指针指向类的超类的元类。</p>
<p>更奇怪的是，<code>基类</code>的<code>元类</code>将其<code>超类</code>设置为<code>基类本身</code>。</p>
<p>这种继承层次结构的结果是，层次结构中的所有实例、类和元类都继承自层次结构的基类。</p>
<p>对于NSObject层次结构中的所有实例、类和元类，这意味着所有NSObject实例方法都是有效的。对于类和元类，所有NSObject类方法也是有效的。</p>
<p>所有这些在文本中都很混乱。Greg Parker将实例、类、元类和它们的超类以及它们是如何组合在一起的组成了一个很好的图表。</p>
<h1 id="这一点的实验证实"><a href="#这一点的实验证实" class="headerlink" title="这一点的实验证实"></a>这一点的实验证实</h1><p>为了确认这一切，让我们看看我在本文开头给出的ReportFunction的输出。这个函数的目的是跟踪isa指针并记录它所发现的内容。</p>
<p>要运行ReportFunction，我们需要创建动态创建的类的实例，并在其上调用report方法。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">id</span> instanceOfNewClass = [[newClass alloc] initWithDomain:<span class="string">@&quot;someDomain&quot;</span> code:<span class="number">0</span> userInfo:<span class="literal">nil</span>];</span><br><span class="line">[instanceOfNewClass performSelector:<span class="keyword">@selector</span>(report)];</span><br><span class="line">[instanceOfNewClass release];</span><br></pre></td></tr></table></figure>

<p>由于报表方法没有声明，所以我使用performSelector:调用它，因此编译器不会给出警告。</p>
<p>ReportFunction现在将遍历isa指针，并告诉我们使用哪些对象作为类、元类和元类的类。</p>
<blockquote>
<p>获取对象的类:ReportFunction使用object_getClass跟踪isa指针，因为isa指针是该类的受保护成员(您不能直接访问其他对象的isa指针)。ReportFunction不使用类方法来实现这一点，因为在类对象上调用类方法不会返回元类，而是再次返回类(因此[NSString类]将返回NSString类而不是NSString元类)。</p>
</blockquote>
<p>This is the output (minus NSLog prefixes) when the program runs:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">This object is <span class="number">0x10010c810</span>.</span><br><span class="line">Class is RuntimeErrorSubclass, and <span class="keyword">super</span> is <span class="built_in">NSError</span>.</span><br><span class="line">Following the isa pointer <span class="number">1</span> times gives <span class="number">0x10010c600</span></span><br><span class="line">Following the isa pointer <span class="number">2</span> times gives <span class="number">0x10010c630</span></span><br><span class="line">Following the isa pointer <span class="number">3</span> times gives <span class="number">0x7fff71038480</span></span><br><span class="line">Following the isa pointer <span class="number">4</span> times gives <span class="number">0x7fff71038480</span></span><br><span class="line"><span class="built_in">NSObject</span><span class="string">&#x27;s class is 0x7fff71038490</span></span><br><span class="line"><span class="string">NSObject&#x27;</span>s meta <span class="keyword">class</span> is <span class="number">0x7fff71038480</span></span><br></pre></td></tr></table></figure>

<p>反复查看isa值所达到的地址:</p>
<ul>
<li>对象是地址0x10010c810。</li>
<li>类的地址是0x10010c600。</li>
<li>元类是地址0x10010c630。</li>
<li>元类的类(即NSObject元类)是地址0x7fff71038480。</li>
<li>NSObject元类的类是它自己。</li>
</ul>
<p>地址的值并不十分重要，除非它显示了从类到元类到NSObject元类的过程。</p>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>元类是类对象的类。每个类都有自己独特的元类(因为每个类都可以有自己独特的方法列表)。这意味着所有类对象本身并不都属于同一个类。</p>
<p>元类将始终确保类对象具有层次结构中基类的所有实例和类方法，以及中间的所有类方法。对于派生自NSObject的类，这意味着所有NSObject实例和协议方法都为所有类(和元类)对象定义。</p>
<p>所有元类本身都使用基类的元类(NSObject层次结构类的NSObject元类)作为类，包括运行时中惟一自定义的基类元类。</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://wangkejie.gitee.io/blogwebpage/iOS/origincode/origincodestudy.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blogwebpage/images/avatar.gif">
      <meta itemprop="name" content="Jack Wang">
      <meta itemprop="description" content="书山有路勤为径，学海无涯苦作舟！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="王科杰">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/blogwebpage/iOS/origincode/origincodestudy.html" class="post-title-link" itemprop="url">iOS类的本质</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-10-10 11:26:39" itemprop="dateCreated datePublished" datetime="2017-10-10T11:26:39+08:00">2017-10-10</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/" itemprop="url" rel="index"><span itemprop="name">源码学习</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/%E7%B1%BB%E7%9A%84%E6%9C%AC%E8%B4%A8/" itemprop="url" rel="index"><span itemprop="name">类的本质</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h1 id="class"><a href="#class" class="headerlink" title="class"></a>class</h1><p>class_data_bits_t bits 这是一个64位结构体，里面只有一个 uintptr_t bits,<br>在x86_64架构上，并且class_rw_t [2, 47]只占到了47位，如下</p>
<table>
<thead>
<tr>
<th>63</th>
<th>47</th>
<th>2</th>
<th>1</th>
<th>0</th>
</tr>
</thead>
<tbody><tr>
<td>extra</td>
<td>class_rw_t data</td>
<td>requires raw_isa</td>
<td>has default_rr</td>
<td>is_swift</td>
</tr>
</tbody></table>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> FAST_IS_SWIFT           (1UL&lt;&lt;0)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> FAST_HAS_DEFAULT_RR     (1UL&lt;&lt;1)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> FAST_REQUIRES_RAW_ISA   (1UL&lt;&lt;2)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> FAST_DATA_MASK          0x00007ffffffffff8UL</span></span><br></pre></td></tr></table></figure>
<p>在编译期间类的结构中的 <code>class_data_bits_t *data</code> 指向的是一个 <code>class_ro_t *</code> 指针：</p>
<ul>
<li>类在内存中的位置是在编译期间决定的，在之后修改代码，也不会改变内存中的位置。</li>
<li>类的方法、属性以及协议在编译期间存放到了“错误”的位置，直到 realizeClass 执行之后，才放到了 class_rw_t 指向的只读区域 class_ro_t，这样我们即可以在运行时为 class_rw_t 添加方法，也不会影响类的只读结构。</li>
<li>在 class_ro_t 中的属性在运行期间就不能改变了，再添加方法时，会修改 class_rw_t 中的 methods 列表，而不是 class_ro_t 中的 baseMethods，对于方法的添加会在之后的文章中分析。</li>
</ul>
<p>在 Objective-C 运行时 初始化的过程中会对其中的类进行第一次初始化也就是执行 realizeClass 方法，为类分配可读写结构体 class_rw_t 的空间，并返回正确的类结构体。</p>
<p>而 _class_initialize 方法会调用类的 initialize 方法，</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (!cls-&gt;isRealized()) &#123;</span><br><span class="line">    rwlock_writer_t lock(runtimeLock);</span><br><span class="line">    realizeClass(cls);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (initialize  &amp;&amp;  !cls-&gt;isInitialized()) &#123;</span><br><span class="line">    _class_initialize (_class_getNonMetaClass(cls, inst));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>参考 <span class="exturl" data-url="aHR0cHM6Ly9kcmF2ZW5lc3MubWUvb2JqZWN0LWluaXQ=">对象是如何初始化的<i class="fa fa-external-link-alt"></i></span></p>
<h1 id="selector"><a href="#selector" class="headerlink" title="@selector()"></a>@selector()</h1><p>使用 @selector() 生成的选择子不会因为类的不同而改变，其内存地址在编译期间就已经确定了。也就是说向不同的类发送相同的消息时，其生成的选择子是完全相同的。</p>
<ul>
<li>Objective-C 为我们维护了一个巨大的选择子表</li>
<li>在使用 @selector() 时会从这个选择子表中根据选择子的名字查找对应的 SEL。如果没有找到，则会生成一个 SEL 并添加到表中</li>
<li>在编译期间会扫描全部的头文件和实现文件将其中的方法以及使用 @selector() 生成的选择子加入到选择子表中</li>
</ul>
<h1 id="objc-msgsend"><a href="#objc-msgsend" class="headerlink" title="objc_msgsend()"></a>objc_msgsend()</h1><p>Objective-C 中 objc_msgSend 的实现并没有开源，它只存在于 message.h 这个头文件中。</p>
<blockquote>
<p>当编译器遇到一个方法调用时，它会将方法的调用翻译成以下函数中的一个 objc_msgSend、objc_msgSend_stret、objc_msgSendSuper 和 objc_msgSendSuper_stret。 发送给对象的父类的消息会使用 objc_msgSendSuper 有数据结构作为返回值的方法会使用 objc_msgSendSuper_stret 或 objc_msgSend_stret 其它的消息都是使用 objc_msgSend 发送的</p>
</blockquote>
<p>objc_msgSend -&gt; _class_loopupMethodAndLocadCache3 -&gt; lookUpImpOrForward </p>
<p>对于 objc/objc-runtime-new.mm 中 lookupImpOrForward 的总结如下：</p>
<ul>
<li>无锁的缓存查找</li>
<li>如果类没有实现（isRealized）或者初始化（isInitialized），实现或者初始化类</li>
<li>加锁</li>
<li>缓存以及当前类中方法的查找</li>
<li>尝试查找父类的缓存以及方法列表</li>
<li>没有找到实现，尝试方法解析器</li>
<li>进行消息转发</li>
<li>解锁、返回实现</li>
</ul>
<blockquote>
<p>不过因为 _class_lookupMethodAndLoadCache3 传入的 cache = NO，所以这里会直接跳过 if 中代码的执行，在 objc_msgSend 中已经使用汇编代码查找过了。</p>
</blockquote>
<p>如果缓存没有命中 -&gt; 找到之后加入缓存 -&gt; 第二次调用的时候，就不会走lookupImpOrForward,而是汇编查找了缓存，</p>
<p>为了提高消息传递的效率，ObjC 对 objc_msgSend 以及  cache_getImp 使用了汇编语言来编写。</p>
<p><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2RyYXZlbmVzcy9hbmFseXpl">看着看着来到了这里<i class="fa fa-external-link-alt"></i></span></p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://wangkejie.gitee.io/blogwebpage/iOS/origincode/runtime.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blogwebpage/images/avatar.gif">
      <meta itemprop="name" content="Jack Wang">
      <meta itemprop="description" content="书山有路勤为径，学海无涯苦作舟！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="王科杰">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/blogwebpage/iOS/origincode/runtime.html" class="post-title-link" itemprop="url">runtime</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-10-10 11:26:39" itemprop="dateCreated datePublished" datetime="2017-10-10T11:26:39+08:00">2017-10-10</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/" itemprop="url" rel="index"><span itemprop="name">源码学习</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/runtime/" itemprop="url" rel="index"><span itemprop="name">runtime</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h1 id="先说类的本质"><a href="#先说类的本质" class="headerlink" title="先说类的本质"></a>先说类的本质</h1><p>我们都知道类的本质其实就是一个isa指针,我们直接看源码</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> objc_object &#123;</span><br><span class="line">private:</span><br><span class="line">    isa_t isa;</span><br><span class="line"><span class="comment">//从这开始往下不用看了，都是方法</span></span><br><span class="line">public:</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ISA() assumes this is NOT a tagged pointer object</span></span><br><span class="line">    Class ISA();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// getIsa() allows this to be a tagged pointer object</span></span><br><span class="line">    Class getIsa();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// initIsa() should be used to init the isa of new objects only.</span></span><br><span class="line">    <span class="comment">// If this object already has an isa, use changeIsa() for correctness.</span></span><br><span class="line">    <span class="comment">// initInstanceIsa(): objects with no custom RR/AWZ</span></span><br><span class="line">    <span class="comment">// initClassIsa(): class objects</span></span><br><span class="line">    <span class="comment">// initProtocolIsa(): protocol objects</span></span><br><span class="line">    <span class="comment">// initIsa(): other objects</span></span><br><span class="line">    <span class="keyword">void</span> initIsa(Class cls <span class="comment">/*nonpointer=false*/</span>);</span><br><span class="line">    <span class="comment">//、、、下面的省略</span></span><br></pre></td></tr></table></figure>

<blockquote>
<p>isa_t 的isa又是什么？</p>
</blockquote>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">union</span> isa_t &#123;</span><br><span class="line">    isa_t() &#123; &#125;</span><br><span class="line">    isa_t(uintptr_t value) : bits(value) &#123; &#125;</span><br><span class="line"></span><br><span class="line">    Class cls;</span><br><span class="line">    uintptr_t bits;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">if</span> defined(ISA_BITFIELD)</span></span><br><span class="line">    <span class="keyword">struct</span> &#123;</span><br><span class="line">        ISA_BITFIELD;  <span class="comment">// defined in isa.h</span></span><br><span class="line">    &#125;;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>原来就是一个union共用体，包含cls, bits, ISA_BITFIELD, 我们展开ISA_BITFIELD看一下<br>在arm64结构下，</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"># <span class="meta-keyword">if</span> __arm64__</span></span><br><span class="line"><span class="meta">#   <span class="meta-keyword">define</span> ISA_BITFIELD                                                      \</span></span><br><span class="line">      uintptr_t nonpointer        : <span class="number">1</span>;                                       \</span><br><span class="line">      uintptr_t has_assoc         : <span class="number">1</span>;                                       \</span><br><span class="line">      uintptr_t has_cxx_dtor      : <span class="number">1</span>;                                       \</span><br><span class="line">      uintptr_t shiftcls          : <span class="number">33</span>; <span class="comment">/*MACH_VM_MAX_ADDRESS 0x1000000000*/</span> \</span><br><span class="line">      uintptr_t magic             : <span class="number">6</span>;                                       \</span><br><span class="line">      uintptr_t weakly_referenced : <span class="number">1</span>;                                       \</span><br><span class="line">      uintptr_t deallocating      : <span class="number">1</span>;                                       \</span><br><span class="line">      uintptr_t has_sidetable_rc  : <span class="number">1</span>;                                       \</span><br><span class="line">      uintptr_t extra_rc          : <span class="number">19</span></span><br></pre></td></tr></table></figure>
<p>加上注释吧：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> &#123;</span></span><br><span class="line">    <span class="comment">// 0代表普通的指针，存储着Class，Meta-Class对象的内存地址。</span></span><br><span class="line">    <span class="comment">// 1代表优化后的使用位域存储更多的信息。</span></span><br><span class="line">    <span class="keyword">uintptr_t</span> nonpointer        : <span class="number">1</span>; </span><br><span class="line"></span><br><span class="line">   <span class="comment">// 是否有设置过关联对象，如果没有，释放时会更快</span></span><br><span class="line">    <span class="keyword">uintptr_t</span> has_assoc         : <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 是否有C++析构函数，如果没有，释放时会更快</span></span><br><span class="line">    <span class="keyword">uintptr_t</span> has_cxx_dtor      : <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 存储着Class、Meta-Class对象的内存地址信息</span></span><br><span class="line">    <span class="keyword">uintptr_t</span> shiftcls          : <span class="number">33</span>; </span><br><span class="line"></span><br><span class="line">    <span class="comment">// 用于在调试时分辨对象是否未完成初始化</span></span><br><span class="line">    <span class="keyword">uintptr_t</span> magic             : <span class="number">6</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 是否有被弱引用指向过。</span></span><br><span class="line">    <span class="keyword">uintptr_t</span> weakly_referenced : <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 对象是否正在释放</span></span><br><span class="line">    <span class="keyword">uintptr_t</span> deallocating      : <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 引用计数器是否过大无法存储在isa中</span></span><br><span class="line">    <span class="comment">// 如果为1，那么引用计数会存储在一个叫SideTable的类的属性中</span></span><br><span class="line">    <span class="keyword">uintptr_t</span> has_sidetable_rc  : <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 里面存储的值是引用计数器减1</span></span><br><span class="line">    <span class="keyword">uintptr_t</span> extra_rc          : <span class="number">19</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>cls的内存分布可以参考我的另外一片文章, <a href="./iOS/origincode/origincodestudy.html">类的本质</a> </p>
<p>看过我分类的本质也会发现，class_rw_t中的methods是二维数组的结构，并且可读可写，因此可以动态的添加方法，并且更加便于分类方法的添加。参考 category 源码可以发现，attachList函数内通过memmove 和 memcpy两个操作将分类的方法列表合并在本类的方法列表中。那么此时就将分类的方法和本类的方法统一整合到一起了。<br>其实一开始类的方法，属性，成员变量属性协议等等都是存放在class_ro_t中的，当程序运行的时候，需要将分类中的列表跟类初始的列表合并在一起的时，就会将class_ro_t中的列表和分类中的列表合并起来存放在class_rw_t中，也就是说class_rw_t中有部分列表是从class_ro_t里面拿出来的。</p>
<p>类的初始信息本来其实是存储在class_ro_t中的，并且ro本来是指向cls-&gt;data()的，也就是说bits.data()得到的是ro，但是在运行过程中创建了class_rw_t，并将cls-&gt;data指向rw，同时将初始信息ro赋值给rw中的ro。最后在通过setData(rw)设置data。那么此时bits.data()得到的就是rw，之后再去检查是否有分类，同时将分类的方法，属性，协议列表整合存储在class_rw_t的方法，属性及协议列表中。</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://wangkejie.gitee.io/blogwebpage/iOS/study.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blogwebpage/images/avatar.gif">
      <meta itemprop="name" content="Jack Wang">
      <meta itemprop="description" content="书山有路勤为径，学海无涯苦作舟！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="王科杰">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/blogwebpage/iOS/study.html" class="post-title-link" itemprop="url">慕课网学习笔记</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-10-10 11:26:39" itemprop="dateCreated datePublished" datetime="2017-10-10T11:26:39+08:00">2017-10-10</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/iOS%E8%AF%BE%E7%A8%8B%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/" itemprop="url" rel="index"><span itemprop="name">iOS课程学习笔记</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <p><span class="exturl" data-url="aHR0cHM6Ly9jb2RpbmcuaW1vb2MuY29tL2NsYXNzLzIwMi5odG1s">学习网路课程地址<i class="fa fa-external-link-alt"></i></span>,  以下是笔记</p>
<h1 id="高级"><a href="#高级" class="headerlink" title="高级"></a>高级</h1><ul>
<li>解决研发过程中的<code>关键问题</code>和<code>技术难题</code></li>
<li><code>调优</code>设备流量、性能、电量</li>
<li><code>较强</code>的软件设计能力</li>
<li>对iOS内部原理有<code>深刻理解</code></li>
</ul>
<h1 id="资深"><a href="#资深" class="headerlink" title="资深"></a>资深</h1><ul>
<li><code>精通</code>高性能编程以及性能调优</li>
<li><code>灵活运用</code>数据结构、算法解决<code>复杂程序设计</code>问题</li>
<li>提供性能优化、日志搜索、统计分析等<code>方案</code></li>
<li>架构、模块<code>设计</code></li>
</ul>
<h1 id="简历"><a href="#简历" class="headerlink" title="简历"></a>简历</h1><ul>
<li>排版清晰</li>
<li>重要和突出的表达</li>
<li>线上bug数变化、量化指标 质量上的指标</li>
<li>开发成本节约了多少 量化指标</li>
<li>基本信息、工作经历、项目经验、擅长技能</li>
<li>项目经验(主导、参与)，背景、方案、效果</li>
</ul>
          <!--noindex-->
            <div class="post-button">
              <a class="btn" href="/blogwebpage/iOS/study.html#more" rel="contents">
                阅读全文 &raquo;
              </a>
            </div>
          <!--/noindex-->
        
      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://wangkejie.gitee.io/blogwebpage/iOS/index.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blogwebpage/images/avatar.gif">
      <meta itemprop="name" content="Jack Wang">
      <meta itemprop="description" content="书山有路勤为径，学海无涯苦作舟！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="王科杰">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/blogwebpage/iOS/index.html" class="post-title-link" itemprop="url">iOS技术收集</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-09-15 11:26:39" itemprop="dateCreated datePublished" datetime="2017-09-15T11:26:39+08:00">2017-09-15</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h2 id="这个是iOS技术汇总"><a href="#这个是iOS技术汇总" class="headerlink" title="这个是iOS技术汇总"></a>这个是iOS技术汇总</h2><ul>
<li><a href="./iOS/RunTime">RunTime</a></li>
<li><a href="./iOS/RunLoop">RunLoop</a></li>
</ul>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://wangkejie.gitee.io/blogwebpage/iOS/RunLoop/index.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blogwebpage/images/avatar.gif">
      <meta itemprop="name" content="Jack Wang">
      <meta itemprop="description" content="书山有路勤为径，学海无涯苦作舟！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="王科杰">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/blogwebpage/iOS/RunLoop/index.html" class="post-title-link" itemprop="url">RunLoop相关知识点收集</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-09-15 11:26:39" itemprop="dateCreated datePublished" datetime="2017-09-15T11:26:39+08:00">2017-09-15</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/RunLoop/" itemprop="url" rel="index"><span itemprop="name">RunLoop</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <p>下面是收集的RunLoop相关知识点，还有一些常见的问题或者用例<a href="./iOS/RunLoop/runloop.html">点击这里查看</a></p>
<p><a href="./iOS/RunLoop/runloopfanyi.html">官方文档翻译</a></p>
<h2 id="一、RunLoop概念"><a href="#一、RunLoop概念" class="headerlink" title="一、RunLoop概念"></a>一、RunLoop概念</h2><p>RunLoop是通过内部维护的<code>事件循环(Event Loop)</code>来对<code>事件/消息进行管理</code>的一个对象。</p>
<p>1、没有消息处理时，休眠已避免资源占用，由用户态切换到内核态(CPU-内核态和用户态)<br>2、有消息需要处理时，立刻被唤醒，由内核态切换到用户态</p>
<h3 id="RunLoop源码"><a href="#RunLoop源码" class="headerlink" title="RunLoop源码"></a>RunLoop源码</h3><ol>
<li>NSRunLoop 是基于 CFRunLoopRef 的封装，提供了面向对象的 API，但是这些 API 不是线程安全的。</li>
<li>CFRunLoopRef 的代码是开源的，你可以在这里 <span class="exturl" data-url="aHR0cDovL29wZW5zb3VyY2UuYXBwbGUuY29tL3RhcmJhbGxzL0NGLw==">http://opensource.apple.com/tarballs/CF/<i class="fa fa-external-link-alt"></i></span> 下载到整个 CoreFoundation 的源码来查看。</li>
<li>Swift 开源后，苹果又维护了一个跨平台的 CoreFoundation 版本：<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2FwcGxlL3N3aWZ0LWNvcmVsaWJzLWZvdW5kYXRpb24vJUVGJUJDJThDJUU4JUJGJTk5JUU0JUI4JUFBJUU3JTg5JTg4JUU2JTlDJUFDJUU3JTlBJTg0JUU2JUJBJTkwJUU3JUEwJTgxJUU1JThGJUFGJUU4JTgzJUJEJUU1JTkyJThDJUU3JThFJUIwJUU2JTlDJTg5">https://github.com/apple/swift-corelibs-foundation/，这个版本的源码可能和现有<i class="fa fa-external-link-alt"></i></span> iOS 系统中的实现略不一样，但更容易编译，而且已经适配了 Linux/Windows。</li>
</ol>
<h3 id="为什么main函数不会退出？"><a href="#为什么main函数不会退出？" class="headerlink" title="为什么main函数不会退出？"></a>为什么main函数不会退出？</h3><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> main(<span class="keyword">int</span> argc, <span class="keyword">char</span> * argv[]) &#123;</span><br><span class="line">    <span class="keyword">@autoreleasepool</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">UIApplicationMain</span>(argc, argv, <span class="literal">nil</span>, <span class="built_in">NSStringFromClass</span>([AppDelegate <span class="keyword">class</span>]));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>UIApplicationMain内部默认开启了主线程的RunLoop，并执行了一段无限循环的代码（不是简单的for循环或while循环）</p>
<figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//无限循环代码模式(伪代码)</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span> * argv[])</span> </span>&#123;        </span><br><span class="line">    BOOL <span class="built_in">running</span> = YES;</span><br><span class="line">    <span class="keyword">do</span> &#123;</span><br><span class="line">        <span class="comment">// 执行各种任务，处理各种事件</span></span><br><span class="line">        <span class="comment">// ......</span></span><br><span class="line">    &#125; <span class="keyword">while</span> (<span class="built_in">running</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>UIApplicationMain函数一直没有返回，而是不断地接收处理消息以及等待休眠，所以运行程序之后会保持持续运行状态。</p>
<h2 id="二、RunLoop的数据结构"><a href="#二、RunLoop的数据结构" class="headerlink" title="二、RunLoop的数据结构"></a>二、RunLoop的数据结构</h2><p><code>NSRunLoop(Foundation)</code>是<code>CFRunLoop(CoreFoundation)</code>的封装，提供了面向对象的API<br>RunLoop 相关的主要涉及五个类：</p>
<p><code>CFRunLoop</code>：RunLoop对象<br><code>CFRunLoopMode</code>：运行模式<br><code>CFRunLoopSource</code>：输入源/事件源<br><code>CFRunLoopTimer</code>：定时源<br><code>CFRunLoopObserver</code>：观察者</p>
<p><strong>1、CFRunLoop</strong></p>
<p>由<code>pthread</code>(线程对象，说明<code>RunLoop和线程是一一对应</code>的)、<code>currentMode</code>(当前所处的运行模式)、<code>modes</code>(多个运行模式的集合)、<code>commonModes</code>(模式名称字符串集合)、<code>commonModelItems</code>(Observer,Timer,Source集合)构成</p>
<p><strong>2、CFRunLoopMode</strong></p>
<p>由name、source0、source1、observers、timers构成</p>
<p><strong>3、CFRunLoopSource</strong></p>
<p>分为source0和source1两种</p>
<ul>
<li><code>source0:</code><br>即非基于port的，也就是用户触发的事件。需要手动唤醒线程，将当前线程从内核态切换到用户态</li>
</ul>
<ul>
<li><code>source1:</code><br>基于port的，包含一个 mach_port 和一个回调，可监听系统端口和通过内核和其他线程发送的消息，能主动唤醒RunLoop，接收分发系统事件。<br>具备唤醒线程的能力</li>
</ul>
<p><strong>4、CFRunLoopTimer</strong></p>
<p>基于时间的触发器，基本上说的就是NSTimer。在预设的时间点唤醒RunLoop执行回调。因为它是基于RunLoop的，因此它不是实时的（就是NSTimer 是不准确的。 因为RunLoop只负责分发源的消息。如果线程当前正在处理繁重的任务，就有可能导致Timer本次延时，或者少执行一次）。</p>
<p><strong>5、CFRunLoopObserver</strong></p>
<p>监听以下时间点:<code>CFRunLoopActivity</code></p>
<ul>
<li><p><code>kCFRunLoopEntry</code><br>RunLoop准备启动</p>
</li>
<li><p><code>kCFRunLoopBeforeTimers</code><br>RunLoop将要处理一些Timer相关事件</p>
</li>
<li><p><code>kCFRunLoopBeforeSources</code><br>RunLoop将要处理一些Source事件</p>
</li>
<li><p><code>kCFRunLoopBeforeWaiting</code><br>RunLoop将要进行休眠状态,即将由用户态切换到内核态</p>
</li>
<li><p><code>kCFRunLoopAfterWaiting</code><br>RunLoop被唤醒，即从内核态切换到用户态后</p>
</li>
<li><p><code>kCFRunLoopExit</code><br>RunLoop退出</p>
</li>
<li><p><code>kCFRunLoopAllActivities</code><br>监听所有状态</p>
</li>
</ul>
<p><strong>6、各数据结构之间的联系</strong></p>
<p>线程和RunLoop一一对应， RunLoop和Mode是一对多的，Mode和source、timer、observer也是一对多的</p>
<h2 id="三、RunLoop的Mode"><a href="#三、RunLoop的Mode" class="headerlink" title="三、RunLoop的Mode"></a>三、RunLoop的Mode</h2><p>关于Mode首先要知道一个RunLoop 对象中可能包含多个Mode，且每次调用 RunLoop 的主函数时，只能指定其中一个 Mode(CurrentMode)。切换 Mode，需要重新指定一个 Mode 。主要是为了分隔开不同的 Source、Timer、Observer，让它们之间互不影响。</p>
<p>当RunLoop运行在Mode1上时，是无法接受处理Mode2或Mode3上的Source、Timer、Observer事件的</p>
<p>总共是有五种<code>CFRunLoopMode</code>:</p>
<ul>
<li><code>kCFRunLoopDefaultMode</code>：默认模式，主线程是在这个运行模式下运行</li>
<li><code>UITrackingRunLoopMode</code>：跟踪用户交互事件（用于 ScrollView 追踪触摸滑动，保证界面滑动时不受其他Mode影响）</li>
<li><code>UIInitializationRunLoopMode</code>：在刚启动App时第进入的第一个 Mode，启动完成后就不再使用</li>
<li><code>GSEventReceiveRunLoopMode</code>：接受系统内部事件，通常用不到</li>
<li><code>kCFRunLoopCommonModes</code>：伪模式，不是一种真正的运行模式，是同步Source/Timer/Observer到多个Mode中的一种解决方案</li>
</ul>
<h2 id="四、RunLoop的实现机制"><a href="#四、RunLoop的实现机制" class="headerlink" title="四、RunLoop的实现机制"></a>四、RunLoop的实现机制</h2><p>对于RunLoop而言最核心的事情就是保证线程在没有消息的时候休眠，在有消息时唤醒，以提高程序性能。RunLoop这个机制是依靠系统内核来完成的（苹果操作系统核心组件Darwin中的Mach）。</p>
<p>RunLoop通过<code>mach_msg()</code>函数接收、发送消息。它的本质是调用函数<code>mach_msg_trap()</code>，相当于是一个系统调用，会触发内核状态切换。在用户态调用 <code>mach_msg_trap()</code>时会切换到内核态；内核态中内核实现的<code>mach_msg()</code>函数会完成实际的工作。<br>即基于port的source1，监听端口，端口有消息就会触发回调；而source0，要手动标记为待处理和手动唤醒RunLoop</p>
<p><span class="exturl" data-url="aHR0cHM6Ly93d3cuamlhbnNodS5jb20vcC9hNzY0YWFkMzE4NDc=">Mach消息发送机制<i class="fa fa-external-link-alt"></i></span><br>大致逻辑为：<br>1、通知观察者 RunLoop 即将启动。<br>2、通知观察者即将要处理Timer事件。<br>3、通知观察者即将要处理source0事件。<br>4、处理source0事件。<br>5、如果基于端口的源(Source1)准备好并处于等待状态，进入步骤9。<br>6、通知观察者线程即将进入休眠状态。<br>7、将线程置于休眠状态，由用户态切换到内核态，直到下面的任一事件发生才唤醒线程。</p>
<ul>
<li>一个基于 port 的Source1 的事件(图里应该是source0)。</li>
<li>一个 Timer 到时间了。</li>
<li>RunLoop 自身的超时时间到了。</li>
<li>被其他调用者手动唤醒。</li>
</ul>
<p>8、通知观察者线程将被唤醒。<br>9、处理唤醒时收到的事件。</p>
<ul>
<li>如果用户定义的定时器启动，处理定时器事件并重启RunLoop。进入步骤2。</li>
<li>如果输入源启动，传递相应的消息。</li>
<li>如果RunLoop被显示唤醒而且时间还没超时，重启RunLoop。进入步骤2</li>
</ul>
<p>10、通知观察者RunLoop结束。</p>
<h2 id="五、RunLoop与NSTimer"><a href="#五、RunLoop与NSTimer" class="headerlink" title="五、RunLoop与NSTimer"></a>五、RunLoop与NSTimer</h2><p>一个比较常见的问题：滑动tableView时，定时器还会生效吗？<br>默认情况下RunLoop运行在<code>kCFRunLoopDefaultMode</code>下，而当滑动tableView时，RunLoop切换到<code>UITrackingRunLoopMode</code>，而Timer是在<code>kCFRunLoopDefaultMode</code>下的，就无法接受处理Timer的事件。<br>怎么去解决这个问题呢？把Timer添加到UITrackingRunLoopMode上并不能解决问题，因为这样在默认情况下就无法接受定时器事件了。<br>所以我们需要把Timer同时添加到<code>UITrackingRunLoopMode</code>和<code>kCFRunLoopDefaultMode</code>上。<br>那么如何把timer同时添加到多个mode上呢？就要用到<code>NSRunLoopCommonModes</code>了</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-attr">[[NSRunLoop currentRunLoop]</span> <span class="selector-tag">addTimer</span><span class="selector-pseudo">:timer</span> <span class="selector-tag">forMode</span><span class="selector-pseudo">:NSRunLoopCommonModes</span>];</span><br></pre></td></tr></table></figure>

<p>Timer就被添加到多个mode上，这样即使RunLoop由<code>kCFRunLoopDefaultMode</code>切换到<code>UITrackingRunLoopMode</code>下，也不会影响接收Timer事件</p>
<p>CFRunLoopTimerRef与NSTimer是 toll-free bridged的</p>
<ol>
<li>对于重复的NSTimer，其多次触发的时刻不是一开始算好的，而是timer触发后计算的。但是计算时参考的是上次应当触发的时间_fireTSR，因此计算出的下次触发的时刻不会有误差。</li>
<li>设置了tolerance的NSTimer，对于iOS和MacOS系统，实质上会采用GCD timer的形式注册到内核中，GCD timer触发后，再由RunLoop处理其回调逻辑。对于没有设置tolerance的timer，则是用mk_timer的形式注册。</li>
<li>RunLoopMode中timer的排序是按照_fireTSR，也就是应当触发的时间排序的。而且，出于对于保证timer严格有序的考虑，保证时间考前的tolerance较大的timer不会影响后面的timer，系统在给GCD timer 传dummy字段时候会保证_fireTSR+dummy小于后面timer的最晚触发时间。</li>
<li>RunLoop层在timer触发后进行回调的时候，不会对tolerance进行验证。也就是说，因为RunLoop忙导致的timer触发时刻超出了tolerance的情况下，timer并不会取消，而不执行回调。</li>
<li>对于RunLoop忙时很长（或者timeInteval很短）的情况，会导致本该在这段时间内触发的几次回调中，只触发一次，也就是说，这种情况下还是会损失回调的次数。</li>
<li>对于RunLoop比较忙的情况，timer的回调时刻有可能不准，且不会受到tolerance的任何限制。tolerance的作用不是决定timer是否触发的标准，而是一个传递给系统的数值，帮助系统合理的规划GCD Timer的mach-port触发时机。设置了tolerance，一定会损失一定的时间精确度，但是可以显著的降低耗电。<br>CFAbsoluteTimeGetCurrent()获取准确的时间</li>
</ol>
<p>请看下节  Runloop 运行</p>
<blockquote>
<p><a href="./iOS/RunLoop/runloopyunxing.html">Runloop 运行</a></p>
</blockquote>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  


  
  <nav class="pagination">
    <a class="extend prev" rel="prev" href="/blogwebpage/"><i class="fa fa-angle-left" aria-label="上一页"></i></a><a class="page-number" href="/blogwebpage/">1</a><span class="page-number current">2</span><a class="page-number" href="/blogwebpage/page/3/">3</a><span class="space">&hellip;</span><a class="page-number" href="/blogwebpage/page/6/">6</a><a class="extend next" rel="next" href="/blogwebpage/page/3/"><i class="fa fa-angle-right" aria-label="下一页"></i></a>
  </nav>



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="Jack Wang"
      src="/blogwebpage/images/avatar.gif">
  <p class="site-author-name" itemprop="name">Jack Wang</p>
  <div class="site-description" itemprop="description">书山有路勤为径，学海无涯苦作舟！</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/blogwebpage/archives/">
        
          <span class="site-state-item-count">52</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/blogwebpage/categories/">
          
        <span class="site-state-item-count">56</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/blogwebpage/tags/">
          
        <span class="site-state-item-count">54</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL0phY2stVmluZw==" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;Jack-Ving"><i class="github fa-fw"></i>GitHub</span>
      </span>
      <span class="links-of-author-item">
        <span class="exturl" data-url="bWFpbHRvOjcyNzg4MTk0NUBxcS5jb20=" title="E-Mail → mailto:727881945@qq.com"><i class="envelope fa-fw"></i>E-Mail</span>
      </span>
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        
  <div class="beian"><span class="exturl" data-url="aHR0cHM6Ly9iZWlhbi5taWl0Lmdvdi5jbg==">京ICP备16036665号-2 </span>
  </div>

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2021</span>
  <span class="with-love">
    <i class="heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Jack Wang</span>
</div>
  <div class="powered-by">由 <span class="exturl theme-link" data-url="aHR0cHM6Ly9oZXhvLmlv">Hexo</span> & <span class="exturl theme-link" data-url="aHR0cHM6Ly90aGVtZS1uZXh0Lm9yZw==">NexT.Gemini</span> 强力驱动
  </div>

        


  <div style="display: none;">
    <script src="//s95.cnzz.com/z_stat.php?id=1277804316&web_id=1277804316"></script>
  </div>






      </div>
    </footer>
  </div>

  
  <script src="/blogwebpage/lib/anime.min.js"></script>
  <script src="/blogwebpage/lib/velocity/velocity.min.js"></script>
  <script src="/blogwebpage/lib/velocity/velocity.ui.min.js"></script>

<script src="/blogwebpage/js/utils.js"></script>

<script src="/blogwebpage/js/motion.js"></script>


<script src="/blogwebpage/js/schemes/pisces.js"></script>


<script src="/blogwebpage/js/next-boot.js"></script>




  
  <script>
    (function(){
      var canonicalURL, curProtocol;
      //Get the <link> tag
      var x=document.getElementsByTagName("link");
		//Find the last canonical URL
		if(x.length > 0){
			for (i=0;i<x.length;i++){
				if(x[i].rel.toLowerCase() == 'canonical' && x[i].href){
					canonicalURL=x[i].href;
				}
			}
		}
    //Get protocol
	    if (!canonicalURL){
	    	curProtocol = window.location.protocol.split(':')[0];
	    }
	    else{
	    	curProtocol = canonicalURL.split(':')[0];
	    }
      //Get current URL if the canonical URL does not exist
	    if (!canonicalURL) canonicalURL = window.location.href;
	    //Assign script content. Replace current URL with the canonical URL
      !function(){var e=/([http|https]:\/\/[a-zA-Z0-9\_\.]+\.baidu\.com)/gi,r=canonicalURL,t=document.referrer;if(!e.test(r)){var n=(String(curProtocol).toLowerCase() === 'https')?"https://sp0.baidu.com/9_Q4simg2RQJ8t7jm9iCKT-xh_/s.gif":"//api.share.baidu.com/s.gif";t?(n+="?r="+encodeURIComponent(document.referrer),r&&(n+="&l="+r)):r&&(n+="?l="+r);var i=new Image;i.src=n}}(window);})();
  </script>




  
<script src="/blogwebpage/js/local-search.js"></script>













  

  
  <script src="//cdn.jsdelivr.net/npm/quicklink@1/dist/quicklink.umd.js"></script>
  <script>
      window.addEventListener('load', () => {
      quicklink({
        timeout : 3000,
        priority: true,
        ignores : [uri => uri.includes('#'),uri => uri === 'https://wangkejie.gitee.io/blogwebpage/page/2/',]
      });
      });
  </script>

</body>
</html>
